package api

import (
	"bufio"
	"bytes"
	"github.com/semaphoreui/semaphore/api/helpers"
	"github.com/semaphoreui/semaphore/db"
	"github.com/semaphoreui/semaphore/util"
	log "github.com/sirupsen/logrus"
	"net/http"
)

func getAllRunners(w http.ResponseWriter, r *http.Request) {
	runners, err := helpers.Store(r).GetAllRunners(false, false)

	if err != nil {
		panic(err)
	}

	var result = make([]db.Runner, 0)

	result = append(result, runners...)

	helpers.WriteJSON(w, http.StatusOK, result)
}

type runnerWithToken struct {
	db.Runner
	Token      string `json:"token"`
	PrivateKey string `json:"private_key"`
}

func addGlobalRunner(w http.ResponseWriter, r *http.Request) {
	var runner db.Runner
	if !helpers.Bind(w, r, &runner) {
		return
	}

	runner.ProjectID = nil

	var privateKey []byte

	if runner.PublicKey == nil {
		var b bytes.Buffer
		privateKeyFile := bufio.NewWriter(&b)

		publicKey, err := util.GeneratePrivateKey(privateKeyFile)
		if err != nil {
			helpers.WriteError(w, err)
			return
		}

		err = privateKeyFile.Flush()
		if err != nil {
			helpers.WriteError(w, err)
			return
		}

		privateKey = b.Bytes()

		runner.PublicKey = &publicKey
	}

	newRunner, err := helpers.Store(r).CreateRunner(runner)

	if err != nil {
		log.Warn("Runner is not created: " + err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	helpers.WriteJSON(w, http.StatusCreated, runnerWithToken{
		Runner:     newRunner,
		Token:      newRunner.Token,
		PrivateKey: string(privateKey),
	})
}

func globalRunnerMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		runnerID, err := helpers.GetIntParam("runner_id", w, r)

		if err != nil {
			helpers.WriteJSON(w, http.StatusBadRequest, map[string]string{
				"error": "runner_id required",
			})
			return
		}

		store := helpers.Store(r)

		runner, err := store.GetGlobalRunner(runnerID)

		if err != nil {
			helpers.WriteJSON(w, http.StatusNotFound, map[string]string{
				"error": "Runner not found",
			})
			return
		}

		r = helpers.SetContextValue(r, "runner", &runner)
		next.ServeHTTP(w, r)
	})
}

func getGlobalRunner(w http.ResponseWriter, r *http.Request) {
	runner := helpers.GetFromContext(r, "runner").(*db.Runner)

	helpers.WriteJSON(w, http.StatusOK, runner)
}

func updateGlobalRunner(w http.ResponseWriter, r *http.Request) {
	oldRunner := helpers.GetFromContext(r, "runner").(*db.Runner)

	var runner db.Runner
	if !helpers.Bind(w, r, &runner) {
		return
	}

	store := helpers.Store(r)

	runner.ID = oldRunner.ID
	runner.ProjectID = nil

	err := store.UpdateRunner(runner)

	if err != nil {
		helpers.WriteErrorStatus(w, err.Error(), http.StatusBadRequest)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func clearGlobalRunnerCache(w http.ResponseWriter, r *http.Request) {
	runner := helpers.GetFromContext(r, "runner").(*db.Runner)

	store := helpers.Store(r)

	err := store.ClearRunnerCache(*runner)

	if err != nil {
		helpers.WriteError(w, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func deleteGlobalRunner(w http.ResponseWriter, r *http.Request) {
	runner := helpers.GetFromContext(r, "runner").(*db.Runner)

	store := helpers.Store(r)

	err := store.DeleteGlobalRunner(runner.ID)

	if err != nil {
		helpers.WriteErrorStatus(w, err.Error(), http.StatusBadRequest)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func setGlobalRunnerActive(w http.ResponseWriter, r *http.Request) {
	runner := helpers.GetFromContext(r, "runner").(*db.Runner)

	store := helpers.Store(r)

	var body struct {
		Active bool `json:"active"`
	}

	if !helpers.Bind(w, r, &body) {
		helpers.WriteErrorStatus(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	runner.Active = body.Active

	err := store.UpdateRunner(*runner)

	if err != nil {
		helpers.WriteErrorStatus(w, err.Error(), http.StatusBadRequest)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
